﻿using System.Collections.Generic;
using System.Xml.Linq;
using IcicleFramework.Components.Actions.ActionEffects;
using IcicleFramework.Entities;
using IcicleFramework.GameServices;
using Microsoft.Xna.Framework;

namespace IcicleFramework.Components.Actions
{
    public class ActionComponent : BaseComponent, IActionComponent
    {

        #region Member Variables

        protected Dictionary<string, IAction> actions;

        protected Dictionary<string, IActionEffect> myActionEffects;

        protected Dictionary<string, IActionEffect> attachedActionEffects;

        #endregion


        #region Properties

        public IEnumerator<IAction> Actions
        {
            get { return actions.Values.GetEnumerator(); }
        }

        public IEnumerator<IActionEffect> AttachedActionEffects
        {
            get { return attachedActionEffects.Values.GetEnumerator(); }
        }

        public bool ContinuousActions { get; protected set; }

        protected IActionFactory ActionFactory { get; set; }

        #endregion


        #region Events

        public event OnStartedPerformingActionsHandler OnStartedPerformingActions;

        public event OnStoppedPerformingActionsHandler OnStoppedPerformingActions;

        public event DestroyedHandler OnDestroyed;

        #endregion


        #region Constructors

        public ActionComponent()
        {
            actions = new Dictionary<string, IAction>();
        }

        public ActionComponent(ActionComponent old)
            :base(old)
        {
            foreach (var action in old.actions.Values)
            {
                this.actions.Add(action.Name, (IAction) action.DeepClone());
            }
        }

        #endregion


        #region Starting, Resuming, and Pausing Actions

        public bool StartPerformingAction(IGameObject target, string actionName, IActionData data = null, bool continuous = false)
        {
            bool started = false;

            if (actionName != null && actions.ContainsKey(actionName))
            {
                actions[actionName].Execute(target, data);
                started = true;
            }

            return started;
        }

        public void StopPerformingAction(string actionName)
        {
            if (actionName != null && actions.ContainsKey(actionName))
            {
                actions[actionName].Stop();
            }
        }

        public void PausePerformingAction(string actionName)
        {
            if (actionName != null && actions.ContainsKey(actionName))
            {
                actions[actionName].Pause();
            }
        }

        public void ResumePerformingAction(string actionName)
        {
            if (actionName != null && actions.ContainsKey(actionName))
            {
                actions[actionName].Resume();
            }
        }

        public void StopPerfomingActions()
        {
            foreach (var action in actions.Values)
            {
                action.Stop();
            }
        }

        public void PausePerformingActions()
        {
            foreach (var action in actions.Values)
            {
                action.Pause();
            }
        }

        public void ResumePerformingActions()
        {
            foreach (var action in actions.Values)
            {
                action.Resume();
            }
        }

        #endregion


        #region Action Effects

        public bool AttachActionEffect(IActionEffect actionEffect)
        {
            bool added = false;

            if (!attachedActionEffects.ContainsKey(actionEffect.Name))
            {
                attachedActionEffects.Add(actionEffect.Name, actionEffect);
                actionEffect.OnDetach += OnActionEffectDetach;
                added = true;
            }

            return added;
        }

        void OnActionEffectDetach(IActionEffect source, IActionComponent target)
        {
            source.OnDetach -= OnActionEffectDetach;
            attachedActionEffects.Remove(source.Name);
        }

        public bool DetachActionEffect(IActionEffect actionEffect)
        {
            bool removed = false;

            if (attachedActionEffects.ContainsKey(actionEffect.Name))
            {
                bool detached = attachedActionEffects[actionEffect.Name].Detach();
                removed = detached;
            }

            return removed;
        }

        public void DetachAllActionEffects(bool forceDetach = false)
        {
            foreach (IActionEffect effect in attachedActionEffects.Values)
            {
                effect.Detach();
            }
        }

        #endregion


        public override void Update(GameTime gameTime)
        {
            foreach (var action in actions.Values)
            {
                action.Update(gameTime);
            }
        }

        public override IBaseComponent DeepClone()
        {
            return new ActionComponent(this);
        }

        public override void Deserialize(XElement element)
        {
            ActionFactory = GameServiceManager.GetService<IActionFactory>();

            foreach (var actionElement in element.Element("actions").Elements("action"))
            {
                var action = ActionFactory.GenerateAction(actionElement);

                if (action == null) continue;

                action.Parent = this;
                actions.Add(action.Name, action);
            }
        }
    }
}
